import fire
import logging
from utils.mysql_helper import MySQLClient
from utils.logger_helper import LoggerHelper

APP_NAME = "mysql_user_comparer"
logger = LoggerHelper.get_logger(logger_name=APP_NAME)


class MySQLUserComparer(object):
    def __init__(self, source_host, source_port, source_user, source_password,
                 target_host, target_port, target_user, target_password):
        self.source_client = MySQLClient(
            mysql_host=source_host, mysql_port=source_port,
            mysql_user=source_user, mysql_password=source_password,
            database_name="mysql"
        )
        self.target_client = MySQLClient(
            mysql_host=target_host, mysql_port=target_port,
            mysql_user=target_user, mysql_password=target_password,
            database_name="mysql"
        )
        pass

    @classmethod
    def _get_user_dict(cls, mysql_client: MySQLClient):
        sql_script = """
        select * from mysql.user;
        """
        query_rows = mysql_client.mysql_query(sql_script=sql_script)
        user_dict = dict()
        for query_row in query_rows:
            user_full_name = "'{}'@'{}'".format(query_row["User"], query_row["Host"])
            user_full_psw = "{}::{}::{}".format(query_row.get("plugin", ""), query_row.get("Password", ""),
                                                query_row.get("authentication_string", ""))
            user_dict[user_full_name] = user_full_psw
        return user_dict

    @classmethod
    def _get_user_db_dict(cls, mysql_client: MySQLClient):
        sql_script = "select * from mysql.db"
        query_rows = mysql_client.mysql_query(sql_script=sql_script)
        user_dict = dict()
        for query_row in query_rows:
            user_full_name = "'{}'@'{}::{}'".format(
                query_row["User"], query_row["Host"], query_row["Db"]
            )
            user_rights = [
                query_row["Select_priv"],
                query_row["Insert_priv"],
                query_row["Update_priv"],
                query_row["Delete_priv"],
                query_row["Create_priv"],
                query_row["Drop_priv"],
                query_row["Grant_priv"],
                query_row["References_priv"],
                query_row["Index_priv"],
                query_row["Alter_priv"],
                query_row["Create_tmp_table_priv"],
                query_row["Lock_tables_priv"],
                query_row["Create_view_priv"],
                query_row["Show_view_priv"],
                query_row["Create_routine_priv"],
                query_row["Alter_routine_priv"],
                query_row["Execute_priv"],
                query_row["Event_priv"],
                query_row["Trigger_priv"]
            ]
            user_full_right = ",".join(user_rights)
            user_dict[user_full_name] = user_full_right
        return user_dict

    @classmethod
    def _get_user_table_dict(cls, mysql_client: MySQLClient):
        sql_script = "select * from mysql.tables_priv;"
        query_rows = mysql_client.mysql_query(sql_script=sql_script)
        user_dict = dict()
        for query_row in query_rows:
            user_full_name = "'{}'@'{}::{}::{}'".format(
                query_row["User"], query_row["Host"], query_row["Db"], query_row["Table_name"]
            )
            user_rights = "{},{},{}".format(
                query_row["Grantor"], query_row["Timestamp"],
                query_row["Table_priv"], query_row["Column_priv"]
            )
            user_full_right = ",".join(user_rights)
            user_dict[user_full_name] = user_full_right
        return user_dict

    @classmethod
    def _compare_user_data(cls, target_user_dict, source_user_dict, check_type):
        check_errors = []
        for target_user_name, target_user_data in target_user_dict.items():
            if target_user_name not in source_user_dict:
                check_errors.append("目标帐号{}在源实例不存在".format(target_user_name))
            source_user_data = source_user_dict[target_user_name]
            if target_user_data != source_user_data:
                check_errors.append("目标帐号{}的{}和原实例不同".format(check_type, target_user_name))
        for source_user_name, source_user_data in source_user_dict.items():
            if source_user_name not in target_user_dict:
                check_errors.append("源帐号{}在目标实例不存在".format(source_user_name))
        return check_errors

    def _compare_user_psw(self):
        target_user_dict = self._get_user_dict(mysql_client=self.target_client)
        source_user_dict = self._get_user_dict(mysql_client=self.source_client)
        return self._compare_user_data(
            target_user_dict=target_user_dict,
            source_user_dict=source_user_dict,
            check_type="用户密码"
        )

    def _compare_user_db(self):
        target_user_dict = self._get_user_db_dict(mysql_client=self.target_client)
        source_user_dict = self._get_user_db_dict(mysql_client=self.source_client)
        return self._compare_user_data(
            target_user_dict=target_user_dict,
            source_user_dict=source_user_dict,
            check_type="数据库权限"
        )

    def _compare_user_table(self):
        target_user_dict = self._get_user_table_dict(mysql_client=self.target_client)
        source_user_dict = self._get_user_table_dict(mysql_client=self.source_client)
        return self._compare_user_data(
            target_user_dict=target_user_dict,
            source_user_dict=source_user_dict,
            check_type="数据表权限"
        )

    def compare_users(self):
        logger.info("开始检查")
        check_errors = list()
        check_errors += self._compare_user_psw()
        check_errors += self._compare_user_db()
        check_errors += self._compare_user_table()
        if len(check_errors) > 0:
            logger.info("检查的帐号不一致,详情为:\n{}".format("\n".join(check_errors)))
        else:
            logger.info("源实例和目标实例帐号一致")
        logger.info("完成检查")


def init_logger():
    LoggerHelper.init_logger(logger_name=APP_NAME, logger_level=logging.DEBUG)
    global logger
    logger = LoggerHelper.get_logger(logger_name=APP_NAME)


def main(source_host, source_port, source_user, source_password,
         target_host, target_port, target_user, target_password):
    init_logger()
    user_comparer = MySQLUserComparer(
        source_host, source_port, source_user, source_password,
        target_host, target_port, target_user, target_password
    )
    user_comparer.compare_users()


if __name__ == '__main__':
    fire.Fire(main)
